#include "Affects.h"

Affects::Affects(PKB* p){
	pkb = p;
	st = (*pkb).getStmtTable();
	varT = (*pkb).getVarTable();
	modifiesMap = (*pkb).getFullModifiesMap();
	usesMatrix = (*pkb).getFullUsesMatrix();
	vModifiedByLine = vector<vector<int>>(varT.getSize(), vector<int>(1,-1));
	totalLineNum = (*pkb).getTotalLineNum();
	affectsMatrix = vector<vector<int>>(totalLineNum+1, vector<int>(totalLineNum+1,0));
}

vector<vector<int>> Affects::ComputeAffects(){
	int curLine = 1, prevProc = 0;
	while(curLine <= totalLineNum){
		if(prevProc != st.getLineDetails(curLine).procNum){
			vModifiedByLine = vector<vector<int>>(varT.getSize(), vector<int>(1,-1));
			prevProc = st.getLineDetails(curLine).procNum;
		}
		if(st.getLineDetails(curLine).type == "Assign")
			curLine = DealWithAssign(curLine);
		else if(st.getLineDetails(curLine).type == "While")
			curLine = DealWithWhile(curLine);
		else if(st.getLineDetails(curLine).type == "If")
			curLine = DealWithIf(curLine);
		else if(st.getLineDetails(curLine).type == "Call")
			curLine = DealWithCalls(curLine);
	}

	return affectsMatrix;
}

int Affects::DealWithAssign(int curLine){
	for(int i=0;i<usesMatrix.at(curLine).size();i++){
		if(usesMatrix.at(curLine).at(i) == 1){
			if(vModifiedByLine.at(i).at(0) != -1)
				for(int j=0;j<vModifiedByLine.at(i).size();j++){
					int line = vModifiedByLine.at(i).at(j);
					if(affectsMatrix.at(line).at(curLine) == 0)
						affectsList.push_back(pair<int,int>(line,curLine));
					affectsMatrix.at(line).at(curLine) = 1;
				}
		}
	}

	// Update tracking tables
	int varModified = modifiesMap.at(curLine);
	vModifiedByLine.at(varModified).clear();
	vModifiedByLine.at(varModified).push_back(curLine);

	return curLine+1;
}

int Affects::DealWithWhile(int curLine){
	vector<vector<int>> backUpVMList(vModifiedByLine);
	
	while_t w = st.getWhileDetailFromLine(curLine);

	int i = w.loopStartLine;
	while(i<=w.loopEndLine){
		if(st.getLineDetails(i).type == "Assign")
			i = DealWithAssign(i);
		else if(st.getLineDetails(i).type == "If")
			i = DealWithIf(i);
		else if(st.getLineDetails(i).type == "While")
			i = DealWithWhile(i);
		else if(st.getLineDetails(i).type == "Call")
			i = DealWithCalls(i);
	}

	// Do it second time to deal with the new track list
	i = w.loopStartLine;
	while(i<=w.loopEndLine){
		if(st.getLineDetails(i).type == "Assign")
			i = DealWithAssign(i);
		else if(st.getLineDetails(i).type == "If")
			i = DealWithIf(i);
		else if(st.getLineDetails(i).type == "While")
			i = DealWithWhile(i);
		else if(st.getLineDetails(i).type == "Call")
			i = DealWithCalls(i);
	}

	vModifiedByLine = mergeVMList(backUpVMList, vModifiedByLine);
	return w.loopEndLine+1;
}

int Affects::DealWithIf(int curLine){
	if_t l = st.getIfDetailFromLine(curLine);

	// Back up the tracking list.
	vector<vector<int>> backUpVModList = vector<vector<int>>(vModifiedByLine);
	int i=l.thenStartLine;
	while(i<=l.thenEndLine){
		if(st.getLineDetails(i).type == "Assign")
			i = DealWithAssign(i);
		else if(st.getLineDetails(i).type == "While")
			i = DealWithWhile(i);
		else if(st.getLineDetails(i).type == "If")
			i = DealWithIf(i);
		else if(st.getLineDetails(i).type == "Call")
			i = DealWithCalls(i);
	}
	vector<vector<int>> thenVModList = vector<vector<int>>(vModifiedByLine);
	// Get back the tracking list
	vModifiedByLine = vector<vector<int>>(backUpVModList);

	i=l.elseStartLine;
	while(i<=l.elseEndLine){
		if(st.getLineDetails(i).type == "Assign")
			i = DealWithAssign(i);
		else if(st.getLineDetails(i).type == "While")
			i = DealWithWhile(i);
		else if(st.getLineDetails(i).type == "If")
			i = DealWithIf(i);
		else if(st.getLineDetails(i).type == "Call")
			i = DealWithCalls(i);
	}
	vector<vector<int>> elseVModList = vector<vector<int>>(vModifiedByLine);

	vModifiedByLine = mergeVMList(thenVModList, elseVModList);
	return l.elseEndLine+1;
}

int Affects::DealWithCalls(int curLine){
	int calledProc = (*pkb).getCalls().getCalleeFromLineNum(curLine);
	vector<int> modInProc = (*pkb).getPModifiesTableV1().at(calledProc);

	for(int i=0;i<modInProc.size();i++){
		vModifiedByLine.at(modInProc.at(i)).clear();
		vModifiedByLine.at(modInProc.at(i)).push_back(-1);
	}

	return curLine+1;
}

vector<vector<int>> Affects::mergeVMList(vector<vector<int>> list1, vector<vector<int>> list2){
	vector<vector<int>> result;
	for(int i=0;i<varT.getSize();i++){
		vector<int> l1(list1.at(i)), l2(list2.at(i)), r(l1.size()+l2.size());
		vector<int>::iterator it = set_union(l1.begin(),l1.end(), l2.begin(),l2.end(), r.begin());
		r.resize(it-r.begin());
		if(r.size() > 1 && r.at(0) == -1)
			r.erase(r.begin());
		result.push_back(r);
	}
	return result;
}

vector<pair<int,int>> Affects::GetAffectsList(){
	return affectsList;
}